/* ==============================================================================
*
*   Copyright (c) 2010, Pierre Favier
*   All rights reserved.
*
*   NTasking is free software: you can redistribute it and/or modify it
*   under the terms of the GNU General Public License as published by the
*   Free Software Foundation.
*
*   Redistribution and use in source and binary forms, with or without
*   modification, are permitted provided that the following conditions
*   are met:
*
*   -   Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*
*   -   Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials
*       provided with the distribution.
*
*   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
*   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
*   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
*   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
*   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
*   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
*   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
*   THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*   SUCH DAMAGE.
*
* ============================================================================== */

using System;
using System.Threading;

namespace NTasking.Internals
{

    /// <summary>
    /// The <see cref="MethodAccept"/> define the acceptor-side aspect of a synchronized method call
    /// </summary>
    internal abstract class MethodAccept : ContextBase, IGuardable, IContinuable
    {

        private Func<bool> _barrier;


        /// <summary>
        /// Gets or Sets a delegate which is used to evaluate the barrier associated to the current instance
        /// </summary>
        /// <value>A delegate which is used to evaluate the barrier associated to the current instance</value>
        public Func<bool> Barrier
        {
            get { return _barrier; }
            set
            {
                _barrier = value;
                IsClosed = value != null && !value();
            }
        }


        /// <summary>
        /// Gets or Sets a delegate representing the sequence of statements to be executed after this
        /// instance has been processed.
        /// </summary>
        /// <value>A delegate representing the sequence of statements to be executed.</value>
        public Action Continuation { get; set; }


        /// <summary>
        /// Gets or Sets a delegate representing the exception handler of an accept body
        /// </summary>
        /// <value>A delegate representing the exception handler.</value>
        internal Func<Exception, bool> ExceptionHandler { get; set; }


        /// <summary>
        /// Gets a value indicating whether the current instance can not be called.
        /// </summary>
        /// <value>
        /// - <c>true</c> if the current instance is closed, i.e. its <see cref="IGuardable.Barrier"/>
        ///   evaluates to <c>false</c>,
        /// - <c>false</c> if the open instance is open. i.e. its <see cref="IGuardable.Barrier"/>
        ///   is undefined or evaluates to <c>true</c>,
        /// </value>
        public bool IsClosed { get; set; }


        /// <summary>
        /// Gets or sets the <see cref="SynchronizedMethod"/> method of which this instance is the execution.
        /// </summary>
        /// <value>The <see cref="SynchronizedMethod"/> method of which this instance is the execution.</value>
        public SynchronizedMethod Target { get; set; }


        /// <summary>
        /// Executes the sequence of statements after this instance has executed a call.
        /// </summary>
        public void Continue()
        {
            try
            {
                CheckResult();
            }
            catch (RendezVousError ex)
            {
                if (ExceptionHandler == null || ExceptionHandler(ex.InnerException))
                    throw;
            }
            //catch (Exception ex)
            //{
            //    if (ExceptionHandler == null || ExceptionHandler(ex))
            //        throw;
            //}

            if (Continuation != null)
                Continuation();
        }


        /// <summary>
        /// Determines whether the specified call can be accepted.
        /// </summary>
        /// <param name="call">The call to be checked.</param>
        /// <returns>
        /// <c>true</c> if the specified call can be accepted; otherwise, <c>false</c>.
        /// </returns>
        internal abstract bool IsAcceptable(MethodCall call);


        /// <summary>
        /// Determine whether the specified call is a call to the synchronized method of which this
        /// instance is the execution.
        /// </summary>
        /// <param name="call"></param>
        /// <returns>
        /// <c>true</c> if the specified call is a call to the synchronized method of which
        /// this instance is the execution; <c>false</c> otherwise.
        /// </returns>
        internal virtual bool MatchCall(MethodCall call)
        {
            return call.Target == Target;
        }


        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return Target.ToString();
        }

    }


    /// <summary>
    /// The <see cref="MethodAccept{AcceptDelegateT}"/> define the acceptor-side aspect of a synchronized method call
    /// for methods with a specific parameter profile
    /// </summary>
    /// <typeparam name="AcceptDelegateT">Represents the delegate type defining the parameter profile of the accept's body.</typeparam>
    internal class MethodAccept<AcceptDelegateT> : MethodAccept
        where AcceptDelegateT : class // Should be MulticastDelegate but refused by the compiler
    {

        /// <summary>
        /// Gets or sets a delegate representing the sequence of statements which will be executed when
        /// accepting a call.
        /// </summary>
        /// <value>The sequence of statements which will be executed when accepting a call.</value>
        public AcceptDelegateT Body { get; set; }


        /// <summary>
        /// Gets a value indicating whether this instance has a body to be executed when accpeting a call.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance has a body to be executed when accpeting a call;
        /// <c>false</c> otherwise.
        /// </value>
        internal bool HasBody { get { return Body != null; } }


        /// <summary>
        /// Determines whether the specified call can be accepted.
        /// </summary>
        /// <param name="call">The call to be checked.</param>
        /// <returns>
        ///     <c>true</c> if the specified call can be accepted; otherwise, <c>false</c>.
        /// </returns>
        internal override bool IsAcceptable(MethodCall call)
        {
            return Barrier == null || Barrier();
        }

    }


    /// <summary>
    /// The <see cref="MethodAccept{IndexT, AcceptDelegateT}"/> define the acceptor-side aspect of a synchronized method family call
    /// with a specific parameter profile and index type
    /// </summary>
    /// <typeparam name="IndexT">The type of the method family's index.</typeparam>
    /// <typeparam name="AcceptDelegateT">Represents the delegate type defining the parameter profile of the accept's body.</typeparam>
    internal class MethodAccept<IndexT, AcceptDelegateT> :
        MethodAccept<AcceptDelegateT>,
        IGuardable<IndexT>,
        IIndexed<IndexT>

        where AcceptDelegateT : class // Should be MulticastDelegate but refused by the compiler
    {

        private IndexT _index;


        /// <summary>
        /// Gets or sets the index of this instance within its family.
        /// </summary>
        /// <value>The index of this instance within its family.</value>
        public IndexT Index
        {
            get { return _index; }
            set
            {
                _index = value;
                IndexSet = true;
            }
        }


        /// <summary>
        /// Gets or Sets a delegate allowing to evaluate if this instance can accept calls for a given Index.
        /// </summary>
        /// <value>The delegate allowing to evaluate if this instance can accept calls for a given Index.</value>
        public Func<IndexT, bool> IndexedBarrier { get; set; }


        /// <summary>
        /// Gets or sets a value indicating whether this instance's Index has been set.
        /// </summary>
        /// <value><c>true</c> if [index set]; otherwise, <c>false</c>.</value>
        public bool IndexSet { get; private set; }


        /// <summary>
        /// Determines whether the specified call can be accepted.
        /// </summary>
        /// <param name="call">The call to be checked.</param>
        /// <returns>
        ///     <c>true</c> if the specified call can be accepted; otherwise, <c>false</c>.
        /// </returns>
        internal override bool IsAcceptable(MethodCall call)
        {
            var requeue = (IIndexed<IndexT>)call.Requeued;
            Func<IndexT, bool> barrier = IndexedBarrier;
            bool indexMatch = !IndexSet || Index.Equals(requeue.Index);

            return indexMatch && (barrier == null || barrier(requeue.Index));
        }


        /// <summary>
        /// Determine whether the specified call is a call to the synchronized method of which this
        /// instance is the execution.
        /// </summary>
        /// <param name="call"></param>
        /// <returns>
        ///     <c>true</c> if the specified call is a call to the synchronized method of which
        /// this instance is the execution; <c>false</c> otherwise.
        /// </returns>
        internal override bool MatchCall(MethodCall call)
        {
            return base.MatchCall(call) && Index.Equals(((IIndexed<IndexT>)call).Index);
        }

    }

}
